package syssv

import (
	"database/sql"
	"go.uber.org/zap"
	"sync"
	"wzgames/internal/admin/adc"
	"wzgames/internal/admin/admindao"
	"wzgames/internal/admin/api/wzabind"
	"wzgames/internal/admin/model"
	"wzgames/internal/client"
	"wzgames/internal/constant"
	"wzgames/internal/gloader"
	"wzgames/internal/model/mcomm"
	"wzgames/internal/response"
	"wzgames/pkg/components/mysql"
	"wzgames/pkg/logger"
	"wzgames/pkg/utils/byteutil"
	"wzgames/pkg/utils/dateutil"
)

var (
	sysSv *SysSV
	once  sync.Once
)

type SysSV struct {
	adminDs *mysql.DS
}

func SV() *SysSV {
	if sysSv == nil {
		once.Do(func() {
			sysSv = &SysSV{}
			sysSv.adminDs = gloader.CommMgr().GetDBProxy(constant.MysqlOfAdmin)
		})
	}
	return sysSv
}

func (sv *SysSV) GetCurrencyList(adm *adc.Admin, req wzabind.SearchReq) ([]*model.SysCurrency, int, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	count, e := dao.GetCurrencyTotal(req.Keyword, req.Field1)
	if e != nil {
		return nil, 0, response.ErrInternal
	}
	offset := (req.Page - 1) * req.PageSize
	ret, err := dao.GetCurrencyList(req.Keyword, req.Field1, offset, req.PageSize)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] get currency list Error on GetCurrencyList", zap.Int64("AdminId", adm.AdminId()), zap.Error(err))
			return nil, 0, response.ErrInternal
		}
	}

	return ret, count, 0
}

func (sv *SysSV) CreateCurrency(adm *adc.Admin, req wzabind.SysCurrencyReq) (*model.SysCurrency, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetCurrencyById(req.CurrId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] create currency Error on GetCurrencyById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("CurrId", req.CurrId), zap.String("Curr", req.Curr), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc != nil && cc.CurrId > 0 {
		return nil, response.ErrDuplicate
	}
	t := dateutil.UTC()
	ent := &model.SysCurrency{
		CurrId:    req.CurrId,
		Curr:      req.Curr,
		Name:      req.Name,
		CurrType:  mcomm.CurrencyType(req.CurrType),
		Precision: req.Precision,
		Status:    mcomm.Status(req.Status),
		Created:   t,
		Updated:   t,
	}
	err = dao.CreateSysCurrency(ent)
	if err != nil {
		logger.Error("[SysSV] create currency Error on CreateSysCurrency", zap.Int64("AdminId", adm.AdminId()), zap.Int32("CurrId", req.CurrId), zap.String("Curr", req.Curr), zap.Error(err))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) UpdateCurrency(adm *adc.Admin, req wzabind.SysCurrencyReq) (*model.SysCurrency, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetCurrencyById(req.CurrId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] update currency Error on GetCurrencyById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("CurrId", req.CurrId), zap.String("Curr", req.Curr), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc == nil && cc.CurrId == 0 {
		return nil, response.ErrNotFound
	}
	t := dateutil.UTC()
	ent := &model.SysCurrency{
		CurrId:    req.CurrId,
		Curr:      req.Curr,
		Name:      req.Name,
		CurrType:  mcomm.CurrencyType(req.CurrType),
		Precision: req.Precision,
		Status:    mcomm.Status(req.Status),
		Created:   cc.Created,
		Updated:   t,
	}
	_, e := dao.UpdateSysCurrency(ent)
	if e != nil {
		logger.Error("[SysSV] update currency Error on UpdateSysCurrency", zap.Int64("AdminId", adm.AdminId()), zap.Int32("CurrId", req.CurrId), zap.String("Curr", req.Curr), zap.Error(e))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) GetLangList(adm *adc.Admin, req wzabind.SearchReq) ([]*model.SysLang, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	ret, err := dao.GetSysLangs(req.Keyword)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] get lang list Error on GetSysLangs", zap.Int64("AdminId", adm.AdminId()), zap.Error(err))
			return nil, response.ErrInternal
		}
	}

	return ret, 0
}

func (sv *SysSV) CreateLang(adm *adc.Admin, req wzabind.SysLangReq) (*model.SysLang, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	t := dateutil.UTC()
	ent := &model.SysLang{
		Name:    req.Name,
		Desc:    req.Desc,
		Created: t,
		Updated: t,
	}
	err := dao.CreateSysLang(ent)
	if err != nil {
		logger.Error("[SysSV] create sys lang Error on CreateSysLang", zap.Int64("AdminId", adm.AdminId()), zap.String("Lang", req.Name), zap.Error(err))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) GetGameList(adm *adc.Admin, req wzabind.SearchReq) ([]*model.SysGame, int, int16) {
	dao := admindao.GameDao{Driver: sv.adminDs.Ds()}
	count, e := dao.GetSysGameTotal(req.Field2, req.Keyword, req.Field1)
	if e != nil {
		return nil, 0, response.ErrInternal
	}
	offset := (req.Page - 1) * req.PageSize
	ret, err := dao.GetSysGameList(req.Field2, req.Keyword, req.Field1, offset, req.PageSize)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] get sys game list Error on GetSysGameList", zap.Int64("AdminId", adm.AdminId()), zap.Error(err))
			return nil, 0, response.ErrInternal
		}
	}

	return ret, count, 0
}

func (sv *SysSV) CreateGame(adm *adc.Admin, req wzabind.SysGameReq) (*model.SysGame, int16) {
	dao := admindao.GameDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetSysGameById(req.GameId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] create sys game Error on GetSysGameById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc != nil && cc.GameId > 0 {
		return nil, response.ErrDuplicate
	}
	cc, err = dao.GetSysGameByTag(req.GTag)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] create sys game Error on GetSysGameByTag", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc != nil && cc.GameId > 0 {
		return nil, response.ErrDuplicate
	}
	t := dateutil.UTC()
	ent := &model.SysGame{
		GameId:  req.GameId,
		Name:    req.Name,
		GTag:    req.GTag,
		Langs:   req.Langs,
		Grp:     req.Grp,
		Status:  mcomm.Status(req.Status),
		Created: t,
		Updated: t,
	}
	err = dao.CreateSysGame(ent)
	if err != nil {
		logger.Error("[SysSV] create sys game Error on CreateSysGame", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) UpdateGame(adm *adc.Admin, req wzabind.SysGameReq) (*model.SysGame, int16) {
	dao := admindao.GameDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetSysGameById(req.GameId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] update sys game Error on GetCurrencyById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc == nil && cc.GameId == 0 {
		return nil, response.ErrNotFound
	}

	cc1, err1 := dao.GetSysGameByTag(req.GTag)
	if err1 != nil {
		if err1 != sql.ErrNoRows {
			logger.Error("[SysSV] update sys game Error on GetSysGameByTag", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err1))
			return nil, response.ErrInternal
		}
	}
	if cc1 != nil && cc1.GameId != req.GameId {
		return nil, response.ErrDuplicate
	}

	t := dateutil.UTC()
	ent := &model.SysGame{
		GameId:  req.GameId,
		Name:    req.Name,
		GTag:    req.GTag,
		Langs:   req.Langs,
		Grp:     req.Grp,
		Status:  mcomm.Status(req.Status),
		Created: cc.Created,
		Updated: t,
	}
	_, e := dao.UpdateSysGame(ent)
	if e != nil {
		logger.Error("[SysSV] update sys game Error on UpdateSysGame", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(e))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) UpdateGameLang(adm *adc.Admin, req wzabind.SysGameReq) (*model.SysGame, int16) {
	dao := admindao.GameDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetSysGameById(req.GameId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] update sys game lang Error on GetCurrencyById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc == nil && cc.GameId == 0 {
		return nil, response.ErrNotFound
	}

	t := dateutil.UTC()
	ent := &model.SysGame{
		GameId:  req.GameId,
		Langs:   req.Langs,
		Updated: t,
	}
	_, e := dao.UpdateSysGameLang(ent)
	if e != nil {
		logger.Error("[SysSV] update sys game lang Error on UpdateSysGameLang", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("GameName", req.Name), zap.Error(e))
		return nil, response.ErrInternal
	}
	cc.Langs = req.Langs
	cc.Updated = t

	return cc, 0
}

func (sv *SysSV) GetGameCurrencyList(adm *adc.Admin, req wzabind.SearchReq) ([]*model.SysGameCurrency, int, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	count, e := dao.GetGameCurrencyTotal(req.Keyword, req.Field1)
	if e != nil {
		return nil, 0, response.ErrInternal
	}
	offset := (req.Page - 1) * req.PageSize
	ret, err := dao.GetGameCurrencyList(req.Keyword, req.Field1, offset, req.PageSize)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] get game currency list Error on GetGameCurrencyList", zap.Int64("AdminId", adm.AdminId()), zap.Error(err))
			return nil, 0, response.ErrInternal
		}
	}

	return ret, count, 0
}

func (sv *SysSV) CreateGameCurrency(adm *adc.Admin, req wzabind.SysGameCurrencyReq) (*model.SysGameCurrency, int16) {
	gamedao := admindao.GameDao{Driver: sv.adminDs.Ds()}
	if req.GameId > 0 {
		sysGame, e := gamedao.GetSysGameById(req.GameId)
		if e != nil {
			if e != sql.ErrNoRows {
				logger.Error("[SysSV] create game currency Error on GetSysGameById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("Curr", req.Curr), zap.Error(e))
				return nil, response.ErrInternal
			}
		}
		if sysGame == nil || sysGame.GameId == 0 {
			return nil, response.ErrBadRequest
		}
	}

	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	sysCurr, e1 := dao.GetCurrencyByName(req.Curr)
	if e1 != nil {
		if e1 != sql.ErrNoRows {
			logger.Error("[SysSV] create game currency Error on GetCurrencyByName", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("Curr", req.Curr), zap.Error(e1))
			return nil, response.ErrInternal
		}
	}
	if sysCurr == nil || sysCurr.CurrId == 0 {
		return nil, response.ErrBadRequest
	}

	valType := mcomm.ValueType(req.VType)
	b := byteutil.StringToSliceByte(req.Cfg)
	code := gloader.CheckGameCurrencyLimit(b, req.Curr, req.GameId, valType)
	if code != 0 {
		return nil, code
	}

	gcId := client.GetID(604, 0)
	if gcId == 0 {
		return nil, response.ErrInternal
	}

	t := dateutil.UTC()
	ent := &model.SysGameCurrency{
		GcId:    int64(gcId),
		GameId:  req.GameId,
		CurrId:  sysCurr.CurrId,
		Curr:    req.Curr,
		VType:   valType,
		Desc:    req.Desc,
		Cfg:     req.Cfg,
		Status:  mcomm.Status(req.Status),
		Created: t,
		Updated: t,
	}
	err := dao.CreateSysGameCurrency(ent)
	if err != nil {
		logger.Error("[SysSV] create game currency Error on CreateSysGameCurrency", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("Curr", req.Curr), zap.Error(err))
		return nil, response.ErrInternal
	}

	return ent, 0
}

func (sv *SysSV) UpdateGameCurrency(adm *adc.Admin, req wzabind.SysGameCurrencyReq) (*model.SysGameCurrency, int16) {
	dao := admindao.SysDao{Driver: sv.adminDs.Ds()}
	cc, err := dao.GetGameCurrencyById(req.GcId)
	if err != nil {
		if err != sql.ErrNoRows {
			logger.Error("[SysSV] update game currency Error on GetGameCurrencyById", zap.Int64("AdminId", adm.AdminId()), zap.Int64("GcId", req.GcId), zap.Error(err))
			return nil, response.ErrInternal
		}
	}
	if cc == nil && cc.CurrId == 0 {
		return nil, response.ErrNotFound
	}

	gameName := ""
	if req.GameId > 0 {
		gamedao := admindao.GameDao{Driver: sv.adminDs.Ds()}
		sysGame, e := gamedao.GetSysGameById(req.GameId)
		if e != nil {
			if e != sql.ErrNoRows {
				logger.Error("[SysSV] update game currency Error on GetSysGameById", zap.Int64("AdminId", adm.AdminId()), zap.Int32("GameId", req.GameId), zap.String("Curr", req.Curr), zap.Error(e))
				return nil, response.ErrInternal
			}
		}
		if sysGame == nil || sysGame.GameId == 0 {
			return nil, response.ErrNotFound
		}
		gameName = sysGame.Name
	}

	valType := mcomm.ValueType(req.VType)
	b := byteutil.StringToSliceByte(req.Cfg)
	code := gloader.CheckGameCurrencyLimit(b, cc.Curr, cc.GameId, valType)
	if code != 0 {
		return nil, code
	}
	t := dateutil.UTC()
	ent := &model.SysGameCurrency{
		GcId:     cc.GcId,
		GameId:   req.GameId,
		GameName: gameName,
		CurrId:   cc.CurrId,
		Curr:     cc.Curr,
		VType:    valType,
		Desc:     req.Desc,
		Cfg:      req.Cfg,
		Status:   mcomm.Status(req.Status),
		Created:  cc.Created,
		Updated:  t,
	}
	_, e := dao.UpdateSysGameCurrency(ent)
	if e != nil {
		logger.Error("[SysSV] update game currency Error on UpdateSysGameCurrency", zap.Int64("AdminId", adm.AdminId()), zap.Int64("GcId", req.GcId), zap.Error(e))
		return nil, response.ErrInternal
	}

	return ent, 0
}
